package io.lvdaxian.middleware.db.router.util;

import org.springframework.core.env.Environment;
import org.springframework.core.env.PropertyResolver;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

/**
 * 这是 属性工具类
 *
 * @author lihh
 */
public class PropertyUtil {
  
  private static int springBootVersion = 1;
  
  static {
    try {
      
      /*
        org.springframework.boot.bind.RelaxedPropertyResolver 是 Spring Boot 提供的一个工具类，用于处理松散绑定的属性解析。
        它可以帮助在应用程序中轻松地解析和访问配置属性，支持对属性名的大小写不敏感、使用点号分隔的嵌套属性等特性。
        以下是 RelaxedPropertyResolver 的主要作用和用法：
        松散绑定：RelaxedPropertyResolver 支持对属性名进行松散绑定，即不区分大小写，可以使用不同的大小写形式来访问同一个属性。
        嵌套属性：支持访问嵌套属性，可以通过点号分隔的方式访问嵌套属性，如 myapp.datasource.url。
        属性值获取：可以通过 getProperty() 方法获取指定属性的值，如果属性不存在，则返回 null。
       */
      Class.forName("org.springframework.boot.bind.RelaxedPropertyResolver");
    } catch (ClassNotFoundException e) {
      springBootVersion = 2;
    }
  }
  
  /**
   * Spring Boot 1.x is compatible with Spring Boot 2.x by Using Java Reflect.
   *
   * @param environment : the environment context
   * @param prefix      : the prefix part of property key
   * @param targetClass : the target class type of result
   * @param <T>         : refer to @param targetClass
   * @return T
   */
  @SuppressWarnings("unchecked")
  public static <T> T handle(final Environment environment, final String prefix, final Class<T> targetClass) {
    switch (springBootVersion) {
      case 1:
        return (T) v1(environment, prefix);
      default:
        return (T) v2(environment, prefix, targetClass);
    }
  }
  
  /**
   * 这是 springboot v1的处理
   *
   * @param environment 上下文环境信息
   * @param prefix      访问前缀
   * @return 这是收集的 map 信息
   * @author lihh
   */
  private static Object v1(final Environment environment, final String prefix) {
    try {
      // 加载 RelaxedPropertyResolver 解析类
      Class<?> resolverClass = Class.forName("org.springframework.boot.bind.RelaxedPropertyResolver");
      // 拿到构造方法
      Constructor<?> resolverConstructor = resolverClass.getDeclaredConstructor(PropertyResolver.class);
      // 这是 sub properties 方法
      Method getSubPropertiesMethod = resolverClass.getDeclaredMethod("getSubProperties", String.class);
      // 根据环境信息 构建实例
      Object resolverObject = resolverConstructor.newInstance(environment);
      // 这是添加后缀 .
      String prefixParam = prefix.endsWith(".") ? prefix : prefix + ".";
      return getSubPropertiesMethod.invoke(resolverObject, prefixParam);
    } catch (final ClassNotFoundException | NoSuchMethodException |
                   SecurityException | InstantiationException |
                   IllegalAccessException | IllegalArgumentException |
                   InvocationTargetException ex) {
      throw new RuntimeException(ex.getMessage(), ex);
    }
  }
  
  private static Object v2(final Environment environment, final String prefix, final Class<?> targetClass) {
    try {
      // 解析属性的工具类
      Class<?> binderClass = Class.forName("org.springframework.boot.context.properties.bind.Binder");
      // 这是 得到get方法
      Method getMethod = binderClass.getDeclaredMethod("get", Environment.class);
      // 这是 得到bind方法
      Method bindMethod = binderClass.getDeclaredMethod("bind", String.class, Class.class);
      // binder 方法
      Object binderObject = getMethod.invoke(null, environment);
      // 剔除 以 . 后缀
      String prefixParam = prefix.endsWith(".") ? prefix.substring(0, prefix.length() - 1) : prefix;
      Object bindResultObject = bindMethod.invoke(binderObject, prefixParam, targetClass);
      Method resultGetMethod = bindResultObject.getClass().getDeclaredMethod("get");
      // 这里执行 bind 方法
      return resultGetMethod.invoke(bindResultObject);
    } catch (final ClassNotFoundException | NoSuchMethodException |
                   SecurityException | IllegalAccessException |
                   IllegalArgumentException | InvocationTargetException ex) {
      throw new RuntimeException(ex.getMessage(), ex);
    }
  }
  
}
